// Menu.java
// By Ned Etcode
// Copyright 1996 Netscape Communications Corp.  All rights reserved.

package netscape.application;

import netscape.util.*;

/** Object subclass managing a collection of MenuItems that appear along the
  * top edge of an ExternalWindow. There are two Menu types: top-level main
  * menus and submenus (a menu invoked by a MenuItem). Top-level menus have an
  * associated java.awt.MenuBar, while submenus have an associated
  * java.awt.Menu.
  * You generally create a new Menu and add MenuItems to it, configuring them
  * with submenus as appropriate. The following code creates a Menu with a
  * single MenuItem containing a single-item submenu:
  * <pre>
  *     menu = new Menu(true);
  *     menuItem = menu.addItemWithSubmenu("Menu One");
  *     menuItem.submenu().addItem("Item One", command, target);
  *     window.setMenu(menu);
  * </pre>
  * <I><b>Note:</b> The Menu class does not support the insertion of a
  * submenu-less MenuItem into a top-level Menu.</i>
  *
  * @see MenuItem
  */

public class Menu {
    java.awt.Menu       awtMenu;
    java.awt.MenuBar    awtMenuBar;
    Application         application;
    Vector              menuItemVector;
    MenuItem            superitem, prototypeItem;
    boolean             isTopLevel;


    /** Constructs a Menu.
      */
    public Menu() {
        this(false);
    }

    /** Constructs a Menu.  If <b>topLevel</b> is <b>true</b>, makes the
      * new Menu a top-level menu.
      */
    public Menu(boolean topLevel) {
        super();
        MenuItem menuItem;

        menuItemVector = new Vector();
        isTopLevel = topLevel;

        if (isTopLevel) {
            awtMenuBar = new java.awt.MenuBar();
        } else {
            awtMenu = new java.awt.Menu("temp");
        }

        menuItem = new MenuItem();
        menuItem.setFont(new Font("Helvetica", Font.BOLD, 14));
        menuItem.setEnabled(true);
        setPrototypeItem(menuItem);
    }

    /** Returns <b>true</b> if the Menu is a top-level menu.
      */
    boolean isTopLevel() {
        return isTopLevel;
    }

    /** Sets the MenuItem that contains this Menu. If this Menu is a top-level
      * Menu, this variable is null. You should never call this method
      * directly.
      */
    void setSuperitem(MenuItem item) {
        superitem = item;
    }

    /** Returns the MenuItem that contains this Menu. If this Menu is a
      * top-level menu, this varaible is null.
      */
    MenuItem superitem() {
        return superitem;
    }

    /** Sets the prototype MenuItem for this Menu.  Whenever the Menu needs a
      * new MenuItem, it clones the prototype MenuItem.
      */
    public void setPrototypeItem(MenuItem prototype) {
        prototypeItem = prototype;
    }

    /** Returns the Menu's prototype MenuItem.
      * @see #setPrototypeItem
      */
    public MenuItem prototypeItem() {
        return prototypeItem;
    }

    /** Creates a new MenuItem with characteristics derived from the prototype.
      * This MenuItem is not added to the Menu.
      */
    MenuItem createItem() {
        MenuItem menuItem;

        menuItem = (MenuItem)prototypeItem().clone();
        menuItem.foundationMenuItem = new FoundationMenuItem("temp", menuItem);
        menuItem.setFont(prototypeItem().font());
        menuItem.setEnabled(prototypeItem().isEnabled());
        return menuItem;
    }

    /** Creates a new Menu for use as a MenuItem's submenu. Called by
      * <b>addItemWithSubmenu()</b>. Subclassers should override this method
      * to return a subclass instance.
      */
    public Menu createMenuAsSubmenu() {
        Menu menu;

        menu = new Menu(false);
        return menu;
    }

    /** Adds a new MenuItem, containing a submenu, to the end of this
      * Menu, with title <b>title</b>.
      */
    public MenuItem addItemWithSubmenu(String title) {
        MenuItem menuItem;
        Menu menu;

        menuItem = createItem();
        menuItem.setTitle(title);

        menu = createMenuAsSubmenu();
        menuItem.setSubmenu(menu);
        addItemAt(menuItem, itemCount());
        return menuItem;
    }

    /** Adds a new MenuItem to the end of this Menu, with title <b>title</b>,
      * command <b>command</b> and Target <b>target</b>.
      */
    public MenuItem addItem(String title, String command, Target target) {
        return addItem(title, (char)0, command, target);
    }

    /** Adds a new MenuItem to the end of this Menu, with title <b>title</b>,
      * command key equivalent <b>key</b>, command <b>command</b> and Target
      * <b>target</b>.
      */
    public MenuItem addItem(String title, char key, String command,
                            Target target) {
        MenuItem menuItem;

        menuItem = createItem();
        menuItem.setSubmenu(null);
        menuItem.setSupermenu(this);
        menuItem.setCommandKey(key);
        menuItem.setTitle(title);
        menuItem.setTarget(target);
        menuItem.setCommand(command);
        addItemAt(menuItem, itemCount());
        return menuItem;
    }

    /** Returns the index of the specified MenuItem.
      */
    public int indexOfItem(MenuItem item) {
        return menuItemVector.indexOf(item);
    }

    /** Returns the number of MenuItems this Menu contains.
      */
    public int itemCount() {
        return menuItemVector.count();
    }

    /** Returns the MenuItem at <b>index</b>.
      */
    public MenuItem itemAt(int index) {
        return (MenuItem)menuItemVector.elementAt(index);
    }

    /** Adds the MenuItem <b>menuItem</b> at <b>index</b>.
      */
    public void addItemAt(MenuItem menuItem, int index) {
        java.awt.Menu menu;
        int i;

        menuItem.setSupermenu(this);
        if (menuItem.hasSubmenu()) {
            menu = menuItem.submenu().awtMenu();

            menu.setLabel(menuItem.title());
            menu.setFont(AWTCompatibility.awtFontForFont(menuItem.font()));
            if (isTopLevel) {
                awtMenuBar.add(menu);
            } else {
                awtMenu.add(menu);
            }
        } else {
            if (isTopLevel) {
                // this will be an ugly hack to allow top level MenuItems
            } else {
                awtMenu.add(menuItem.foundationMenuItem());
            }
        }
        menuItemVector.insertElementAt(menuItem, index);
        for (i = 0; i < itemCount(); i++) {
            itemAt(i).setTitle(itemAt(i).title());
        }
    }

    /** Removes <b>menuItem</b> from the Menu.
      */
    public void removeItem(MenuItem menuItem) {
        menuItemVector.removeElement(menuItem);
        if (isTopLevel()) {
            awtMenuBar.remove(menuItem.foundationMenuItem());
        } else {
            awtMenu.remove(menuItem.foundationMenuItem());
        }
    }

    /** Removes the MenuItem at <b>index</b>.
      */
    public void removeItemAt(int index) {
        menuItemVector.removeElementAt(index);
        if (isTopLevel()) {
            awtMenuBar.remove(index);
        } else {
            awtMenu.remove(index);
        }
    }

    /** Replaces the MenuItem at <b>index</b> with <b>menuItem</b>.
      */
    /*
    public void replaceItemAt(int index, MenuItem menuItem) {
        menuItemVector.replaceElementAt(index, menuItem);
    }
    */

    /** Replaces the MenuItem with the new MenuItem.
      */
    /*
    public void replaceItem(MenuItem item, MenuItem newItem) {
        int i;

        i = indexOfItem(item);
        if (i != -1) {
            replaceItemAt(i, newItem);
        }
    }
    */

    /** Examines all MenuItems for a command key equivalent.  If the Menu
      * finds a match, this method performs the corresponding command and
      * returns <b>true</b>.
      */
    public boolean handleCommandKeyEvent(KeyEvent event) {
        int          i;
        MenuItem     item;
        boolean      eventHandled = false;

        for (i = 0; i < itemCount() && !eventHandled; i++) {
            item = itemAt(i);
            if (!item.isEnabled()) {
                continue;
            }
            if (item.hasSubmenu()) {
                eventHandled = item.submenu().handleCommandKeyEvent(event);
            } else {
                // ALERT.  this should really compare the event.key to
                // the MenuItem's commandkey as a char, not an int
                if ((int)item.commandKey() == event.key + 64) {
                    item.sendCommand();
                    eventHandled = true;
                }
            }
        }
        return eventHandled;
    }

    /** Returns the string width of the MenuItem with the longest title.
      */
    int maxMenuItemWidth() {
        int          i, width, maxWidth;
        MenuItem     item;
        FontMetrics  fontMetrics;

        for (i = 0, maxWidth = 0; i< itemCount(); i++) {
            item = itemAt(i);
            fontMetrics = item.font().fontMetrics();
            width = fontMetrics.stringWidth(item.title());
            if (width > maxWidth) {
                maxWidth = width;
            }
        }
        return maxWidth;
    }

    /** Returns the java.awt.Menu associated with this Menu. This variable
      * is only set in submenus.
      * @see Menu#awtMenuBar
      */
    java.awt.Menu awtMenu() {
        return awtMenu;
    }

    /** Returns the java.awt.Menu associated with this Menu. This variable
      * is only set if this is a top-level Menu.
      * @see Menu#awtMenu
      */
    java.awt.MenuBar awtMenuBar() {
        return awtMenuBar;
    }

    /** Sets the application associated with this menu. This variable is
      * only set if this is a top-level Menu. You never call this method
      * directly, but rather through ExternalWindow in its setMenu method.
      */
    void setApplication(Application app) {
        application = app;
    }

    /** Returns the application associated with this menu. This variable is
      * only set if this is a top-level Menu.
      */
    Application application() {
        return application;
    }
}
